4.7 🔥 Разработка приложения Sushi App. Часть 1
6 из 6 шагов пройдено

 Разработка приложения Sushi App. Часть 1

➡️Ссылка на репозиторий с кодом этого урока (ветка main)

Добавление в корзину

Переходим в файл main_screen.dart и добавим ещё одно состояние rollsCart корзины для хранения данных в массиве.

Так же добавим метод добавления в корзину addToCart который передадим как колбэк виджету RollCard

Файл main_screen.dart

import 'package:flutter/material.dart';  
import 'package:sushi_app/widgets/roll_card.dart';  
  
import '../models/roll.dart';  
import '../data/rolls_data.dart';  
  
  
class MainScreen extends StatefulWidget {  
  MainScreen({super.key});  
  
  @override  
  State<MainScreen> createState() => _MainScreenState();  
}  
  
class _MainScreenState extends State<MainScreen> {  
  final List<Roll> rolls = data; // Данные для списка роллов  
  
  final List<Roll> rollsCart = []; // Список роллов в корзине  
  
  // Функция для добавления ролла в корзину  
  // После запускаем обновление UI через setState  
  void addToCart(Roll roll) {  
    setState(() {  
      rollsCart.add(roll);  
    });  
  }  
  
  @override  
  Widget build(BuildContext context) {  
    return Scaffold(  
      appBar: AppBar(  
        title: const Text('Sushi Shop'),  
        actions: <Widget>[  
          IconButton(  
            // Значение Badge теперь будет показывать количество данных в корзине
            icon: Badge.count(
              count: rollsCart.length, 
              child: const Icon(Icons.shopping_cart)
            ),  
            tooltip: 'Корзина',  
            // Чтобы Badge обновился при возврате назад на этот экран  
            onPressed: () async {  
              await Navigator.pushNamed(context, '/cart', arguments: rollsCart);  
              setState(() {});  
            },  
          ),  
        ],  
      ),  
  
      body: GridView.builder(  
        padding: const EdgeInsets.symmetric(horizontal: 12),  
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(  
          crossAxisCount: 2, // Указываем количество колонок  
          crossAxisSpacing: 8.0, // Отступ между колонками  
          mainAxisSpacing: 8.0, // Отступ между строками  
          childAspectRatio: 0.85, // Соотношение сторон карточки  
        ),  
        itemCount: rolls.length, // Количество элементов в списке  
        itemBuilder: (context, index) {  
          return RollCard(  
            roll: rolls[index],  
            // Передаем колбэк как параметр  
            onBuy: () => addToCart(rolls[index]), // Добавление ролла в корзину  
          );  
        },  
      ),  
    );  
  }  
}

Добавим в виджет RollCard новый параметр onBuy чтобы принять колбэк, и обработаем добавление в корзину при нажатии на кнопку Купить

Файл roll_card.dart

import 'package:flutter/material.dart';  
  
import '../models/roll.dart';  
import '../screens/detail_roll_screen.dart';  
  
/// Виджет отображает информацию о ролле, переданной в параметре [roll].  
/// Он отображает имя, описание, URL-адрес изображения и цену ролла.  
///  
class RollCard extends StatelessWidget {  
  final Roll roll;  
  final VoidCallback onBuy;  // принимаем колбэк от родительского виджета
  
  const RollCard({super.key, required this.roll, required this.onBuy});  
  
  @override  
  Widget build(BuildContext context) {  
    return InkWell(  
      onTap: () => Navigator.pushNamed(context, '/detail', arguments: roll),  
      child: Card(  
        color: Colors.white,  
        child: Padding(  
          padding: const EdgeInsets.all(8.0),  
          child: Column(  
            mainAxisSize: MainAxisSize.min,  
            crossAxisAlignment: CrossAxisAlignment.start,  
            children: [  
              Expanded(  
                child: ClipRRect(  
                  borderRadius: const BorderRadius.only(  
                    topLeft: Radius.circular(12),  
                    topRight: Radius.circular(12),  
                  ),  
                  child: Image.asset(  
                    roll.imageUrl,  
                    width: double.infinity,  
                    fit: BoxFit.cover,  
                  ),  
                ),  
              ),  
              Expanded(  
                child: ListTile(  
                  contentPadding: EdgeInsets.zero,  
                  title: Text(roll.name),  
                  subtitle: Text(  
                    roll.description,  
                    style: const TextStyle(fontSize: 10),  
                  ),  
                ),  
              ),  
              Row(  
                mainAxisAlignment: MainAxisAlignment.spaceBetween,  
                children: [  
                  Text("${roll.price} руб."),  
                  TextButton(  
                    onPressed: () {  
                      onBuy(); // Вызываем функцию родительского виджета
                    },  
                    child: const Text("Купить"),  
                  ),  
                ],  
              ),  
            ],  
          ),  
        ),  
      ),  
    );  
  }  
}

Отображение корзины

Чтобы перейти в корзину добавим маршрут в таблицу маршрутизации в файле main.dart
В файле main_screen.dart обработаем нажатие на кнопку в AppBar для перехода на экран корзины.

Файл main.dart

import 'package:flutter/material.dart';  

import '/screens/cart_screen.dart';  
import '/screens/detail_roll_screen.dart';  
import '/screens/main_screen.dart';  
  
  
void main() => runApp(const RollApp());  
  
/// Виджет RollApp - это точка входа в приложение по заказу роллов  
/// Он создает MaterialApp, который является основой для  
/// всех остальных виджетов.  
  
class RollApp extends StatelessWidget {  
  const RollApp({super.key});  
  
  @override  
  Widget build(BuildContext context) {  
    return MaterialApp(  
      title: "Суши Shop", // Название приложения  
      theme: ThemeData(  
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), // Цветовая схема  
      ),  
      initialRoute: '/', // Экран который видно самым первым  
      routes: {  
        '/': (context) => MainScreen(), // Главный экран  
        '/detail': (context) => DetailRollScreen(), // Экран детального просмотра  
        '/cart': (context) => CartScreen(), // Экран корзины  
      },  
    );  
  }  
}

Файл main_screen.dart

IconButton(  
  icon: Badge.count(
    count: rollsCart.length, 
    child: const Icon(Icons.shopping_cart)
  ),  
  tooltip: 'Корзина',  
  // Чтобы Badge обновился при возврате назад на этот экран  
  onPressed: () async {  
    await Navigator.pushNamed(context, '/cart', arguments: rollsCart);  
    setState(() {});  
  },  
),

Добавим файл cart_screen.dart для экрана корзины

Получаем на этом экране ссылку на данные с экрана main_screen
Теперь изменения данных через ссылку rollsCart приведёт к изменению данных в переменной rollsCart на экране main_screen.dart

Файл cart_screen.dart

import 'package:flutter/material.dart';  
import '../models/roll.dart';  
  
class CartScreen extends StatefulWidget {  
  const CartScreen({super.key});  
  
  @override  
  State<CartScreen> createState() => _CartScreenState();  
}  
  
class _CartScreenState extends State<CartScreen> {  
  @override  
  Widget build(BuildContext context) {  
  
    // Получаем список роллов из аргументов  
    final List<Roll> rollsCart = ModalRoute.of(context)!.settings.arguments as List<Roll>;  
  
    // Вычисляем общую сумму  
    double totalPrice = 0.0;  
    for (var roll in rollsCart) {  
      totalPrice += roll.price;  
    }  
  
    return Scaffold(  
      appBar: AppBar(  
        title: const Text("Корзина"),  
      ),  
      body: Column(  
        children: [  
          Expanded(  
            child: ListView.builder(  
              itemCount: rollsCart.length,  
              itemBuilder: (context, index) {  
                final roll = rollsCart[index];  
                return ListTile(  
                  leading: Image.asset(roll.imageUrl, width: 50, height: 50, fit: BoxFit.cover),  
                  title: Text(roll.name),  
                  subtitle: Text('${roll.price.toStringAsFixed(2)} ₽'),  
                  trailing: IconButton(  
                    icon: const Icon(Icons.delete),  
                    onPressed: () {  
                      setState(() {  
                        rollsCart.removeAt(index); // Удаляем конкретный товар
                      });  
                    },  
                  ),  
                );  
              },  
            ),  
          ),  
          const Divider(),  
          Padding(  
            padding: const EdgeInsets.all(16.0),  
            child: Column(  
              children: [  
                Text(  
                  'Итого: ${totalPrice.toStringAsFixed(2)} ₽',  
                  style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),  
                ),  
                const SizedBox(height: 12),  
                ElevatedButton(  
                  onPressed: rollsCart.isEmpty ? null : () {  
                    ScaffoldMessenger.of(context).showSnackBar(  
                      const SnackBar(content: Text("Заказ оформлен!")),  
                    );  
                  },  
                  child: const Text("Оформить заказ"),  
                ),  
              ],  
            ),  
          ),  
        ],  
      ),  
    );  
  }  
}


Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий
Комментарий закреплён

Изменен Роман Сергиенко